﻿
Imports System.Text
Imports System.Runtime.CompilerServices

Public Module _array
    <Extension()> Public Function reverse(Of T)(ByVal i() As T) As T()
        If isemptyarray(i) Then
            Return i
        Else
            Dim r() As T = Nothing
            ReDim r(array_size(i) - 1)
            For j As Int32 = 0 To array_size(i) - 1
                copy(r(j), i(array_size(i) - j - 1))
            Next
            Return r
        End If
    End Function

    <Extension()> Public Sub gc_keepalive(Of T)(ByVal v() As T)
        For i As Int32 = 0 To array_size(v) - 1
            GC.KeepAlive(v(i))
        Next
        GC.KeepAlive(v)
    End Sub

    <Extension()> Public Function has(Of T)(ByVal i() As T, ByVal j As T) As Boolean
        Return Not isemptyarray(i) AndAlso Array.IndexOf(i, j) >= 0
    End Function

    Public Sub memcpy(Of T)(ByVal dest() As T, ByVal deststart As Int32,
                            ByVal src() As T, ByVal srcstart As Int32, ByVal count As Int32)
        memmove(dest, deststart, src, srcstart, count)
    End Sub

    Public Sub memcpy(Of T)(ByVal dest() As T, ByVal src() As T)
        memcpy(dest, 0, src, 0, array_size(src))
    End Sub

    Public Sub memcpy(Of T)(ByVal dest() As T, ByVal deststart As Int32, ByVal src() As T)
        memcpy(dest, deststart, src, 0, array_size(src))
    End Sub

    Public Sub memcpy(Of T)(ByVal dest() As T, ByVal src() As T, ByVal count As Int32)
        memcpy(dest, 0, src, 0, count)
    End Sub

    Public Sub memmove(Of T)(ByVal dest() As T, ByVal deststart As Int32,
                             ByVal src() As T, ByVal srcstart As Int32, ByVal count As Int32)
        If count > 0 Then
            Array.Copy(src, srcstart, dest, deststart, count)
        End If
    End Sub

    Public Sub memset(Of T)(ByVal dest() As T, ByVal start As Int32, ByVal count As Int32, ByVal src As T)
        For i As Int32 = start To start + count - 1
            copy(dest(i), src)
        Next
    End Sub

    Public Sub memset(Of T)(ByVal dest() As T, ByVal src As T)
        memset(dest, 0, array_size(dest), src)
    End Sub

    Public Sub memclr(Of T)(ByVal dest() As T, ByVal start As Int32, ByVal count As Int32)
        If count > 0 Then
            Array.Clear(dest, start, count)
        End If
    End Sub

    Public Sub memclr(Of T)(ByVal dest() As T)
        memclr(dest, 0, array_size(dest))
    End Sub

    Public Function memcmp(Of T)(ByVal first() As T,
                                 ByVal first_start As Int64,
                                 ByVal second() As T,
                                 ByVal second_start As Int64,
                                 ByVal len As Int64,
                                 ByVal compare As Func(Of T, T, Int32)) As Int32
        If compare Is Nothing OrElse len = 0 Then
            Return 0
        Else
            k_assert(Not first Is Nothing AndAlso first.Length() >= first_start + len,
                     "first is not long enough to first_start + len.")
            k_assert(Not second Is Nothing AndAlso second.Length() >= second_start + len,
                     "second is not long enough to second_start + len.")

            For i As Int64 = 0 To len - 1
                Dim cmp As Int32 = 0
                cmp = compare(first(i + first_start), second(i + second_start))
                If cmp <> 0 Then
                    Return cmp
                End If
            Next

            Return 0
        End If
    End Function

    Public Function memcmp(Of T)(ByVal first() As T,
                                 ByVal first_start As Int64,
                                 ByVal second() As T,
                                 ByVal second_start As Int64,
                                 ByVal len As Int64) As Int32
#If 0 Then
        If istype(Of Byte(), T())(first) AndAlso istype(Of Byte(), T())(second) Then
            Return memcmp(cast(Of Byte())(first), first_start, cast(Of Byte())(second), second_start, len)
        ElseIf istype(Of Int16(), T())(first) AndAlso istype(Of Int16(), T())(second) Then
            Return memcmp(cast(Of Int16())(first), first_start, cast(Of Int16())(second), second_start, len)
        ElseIf istype(Of Int32(), T())(first) AndAlso istype(Of Int32(), T())(second) Then
            Return memcmp(cast(Of Int32())(first), first_start, cast(Of Int32())(second), second_start, len)
        ElseIf istype(Of Int64(), T())(first) AndAlso istype(Of Int64(), T())(second) Then
            Return memcmp(cast(Of Int64())(first), first_start, cast(Of Int64())(second), second_start, len)
        ElseIf istype(Of SByte(), T())(first) AndAlso istype(Of SByte(), T())(second) Then
            Return memcmp(cast(Of SByte())(first), first_start, cast(Of SByte())(second), second_start, len)
        ElseIf istype(Of UInt16(), T())(first) AndAlso istype(Of UInt16(), T())(second) Then
            Return memcmp(cast(Of UInt16())(first), first_start, cast(Of UInt16())(second), second_start, len)
        ElseIf istype(Of UInt32(), T())(first) AndAlso istype(Of UInt32(), T())(second) Then
            Return memcmp(cast(Of UInt32())(first), first_start, cast(Of UInt32())(second), second_start, len)
        ElseIf istype(Of UInt64(), T())(first) AndAlso istype(Of UInt64(), T())(second) Then
            Return memcmp(cast(Of UInt64())(first), first_start, cast(Of UInt64())(second), second_start, len)
        ElseIf istype(Of Single(), T())(first) AndAlso istype(Of Single(), T())(second) Then
            Return memcmp(cast(Of Single())(first), first_start, cast(Of Single())(second), second_start, len)
        ElseIf istype(Of Double(), T())(first) AndAlso istype(Of Double(), T())(second) Then
            Return memcmp(cast(Of Double())(first), first_start, cast(Of Double())(second), second_start, len)
        ElseIf istype(Of String(), T())(first) AndAlso istype(Of String(), T())(second) Then
            Return memcmp(cast(Of String())(first), first_start, cast(Of String())(second), second_start, len)
        Else
            Return memcmp(first, first_start, second, second_start, len, AddressOf compare(Of T, T))
        End If
#Else
        Return memcmp(first, first_start, second, second_start, len, AddressOf compare(Of T, T))
#End If
    End Function

    'speed up
    Public Function memcmp(ByVal first() As Byte,
                           ByVal first_start As Int64,
                           ByVal second() As Byte,
                           ByVal second_start As Int64,
                           ByVal len As Int64) As Int32
        Return memcmp(first, first_start, second, second_start, len, AddressOf compare)
    End Function

    Public Function memcmp(ByVal first() As Int16,
                           ByVal first_start As Int64,
                           ByVal second() As Int16,
                           ByVal second_start As Int64,
                           ByVal len As Int64) As Int32
        Return memcmp(first, first_start, second, second_start, len, AddressOf compare)
    End Function

    Public Function memcmp(ByVal first() As Int32,
                           ByVal first_start As Int64,
                           ByVal second() As Int32,
                           ByVal second_start As Int64,
                           ByVal len As Int64) As Int32
        Return memcmp(first, first_Start, second, second_start, len, AddressOf compare)
    End Function

    Public Function memcmp(ByVal first() As Int64,
                           ByVal first_start As Int64,
                           ByVal second() As Int64,
                           ByVal second_start As Int64,
                           ByVal len As Int64) As Int32
        Return memcmp(first, first_Start, second, second_start, len, AddressOf compare)
    End Function

    Public Function memcmp(ByVal first() As SByte,
                           ByVal first_start As Int64,
                           ByVal second() As SByte,
                           ByVal second_start As Int64,
                           ByVal len As Int64) As Int32
        Return memcmp(first, first_Start, second, second_start, len, AddressOf compare)
    End Function

    Public Function memcmp(ByVal first() As UInt16,
                           ByVal first_start As Int64,
                           ByVal second() As UInt16,
                           ByVal second_start As Int64,
                           ByVal len As Int64) As Int32
        Return memcmp(first, first_Start, second, second_start, len, AddressOf compare)
    End Function

    Public Function memcmp(ByVal first() As UInt32,
                           ByVal first_start As Int64,
                           ByVal second() As UInt32,
                           ByVal second_start As Int64,
                           ByVal len As Int64) As Int32
        Return memcmp(first, first_Start, second, second_start, len, AddressOf compare)
    End Function

    Public Function memcmp(ByVal first() As UInt64,
                           ByVal first_start As Int64,
                           ByVal second() As UInt64,
                           ByVal second_start As Int64,
                           ByVal len As Int64) As Int32
        Return memcmp(first, first_Start, second, second_start, len, AddressOf compare)
    End Function

    Public Function memcmp(ByVal first() As Single,
                           ByVal first_start As Int64,
                           ByVal second() As Single,
                           ByVal second_start As Int64,
                           ByVal len As Int64) As Int32
        Return memcmp(first, first_Start, second, second_start, len, AddressOf compare)
    End Function

    Public Function memcmp(ByVal first() As Double,
                           ByVal first_start As Int64,
                           ByVal second() As Double,
                           ByVal second_start As Int64,
                           ByVal len As Int64) As Int32
        Return memcmp(first, first_Start, second, second_start, len, AddressOf compare)
    End Function

    Public Function memcmp(ByVal first() As Decimal,
                           ByVal first_start As Int64,
                           ByVal second() As Decimal,
                           ByVal second_start As Int64,
                           ByVal len As Int64) As Int32
        Return memcmp(first, first_Start, second, second_start, len, AddressOf compare)
    End Function

    Public Function memcmp(ByVal first() As String,
                           ByVal first_start As Int64,
                           ByVal second() As String,
                           ByVal second_start As Int64,
                           ByVal len As Int64) As Int32
        Return memcmp(first, first_Start, second, second_start, len, AddressOf compare)
    End Function

    Public Function memcmp(Of T)(ByVal first() As T, ByVal second() As T, ByVal len As Int64) As Int32
        Return memcmp(first, 0, second, 0, len)
    End Function

    Public Function memcmp(ByVal first() As Byte, ByVal second() As Byte, ByVal len As Int64) As Int32
        Return memcmp(first, 0, second, 0, len)
    End Function

    Public Function memcmp(ByVal first() As Int16, ByVal second() As Int16, ByVal len As Int64) As Int32
        Return memcmp(first, 0, second, 0, len)
    End Function

    Public Function memcmp(ByVal first() As Int32, ByVal second() As Int32, ByVal len As Int64) As Int32
        Return memcmp(first, 0, second, 0, len)
    End Function

    Public Function memcmp(ByVal first() As Int64, ByVal second() As Int64, ByVal len As Int64) As Int32
        Return memcmp(first, 0, second, 0, len)
    End Function

    Public Function memcmp(ByVal first() As SByte, ByVal second() As SByte, ByVal len As Int64) As Int32
        Return memcmp(first, 0, second, 0, len)
    End Function

    Public Function memcmp(ByVal first() As UInt16, ByVal second() As UInt16, ByVal len As Int64) As Int32
        Return memcmp(first, 0, second, 0, len)
    End Function

    Public Function memcmp(ByVal first() As UInt32, ByVal second() As UInt32, ByVal len As Int64) As Int32
        Return memcmp(first, 0, second, 0, len)
    End Function

    Public Function memcmp(ByVal first() As UInt64, ByVal second() As UInt64, ByVal len As Int64) As Int32
        Return memcmp(first, 0, second, 0, len)
    End Function

    Public Function memcmp(ByVal first() As Single, ByVal second() As Single, ByVal len As Int64) As Int32
        Return memcmp(first, 0, second, 0, len)
    End Function

    Public Function memcmp(ByVal first() As Double, ByVal second() As Double, ByVal len As Int64) As Int32
        Return memcmp(first, 0, second, 0, len)
    End Function

    Public Function memcmp(ByVal first() As String, ByVal second() As String, ByVal len As Int64) As Int32
        Return memcmp(first, 0, second, 0, len)
    End Function

    Private Function memcmp(Of T)(ByVal first() As T,
                                  ByVal second() As T,
                                  ByVal _memcmp As Func(Of T(), T(), Int64, Int32)) As Int32
        If _memcmp Is Nothing Then
            Return 0
        Else
            Dim ll As Int32 = 0
            Dim rl As Int32 = 0
            ll = array_size(first)
            rl = array_size(second)
            If ll <> rl Then
                Return ll.CompareTo(rl)
            Else
                Return _memcmp(first, second, ll)
            End If
        End If
    End Function

    Public Function memcmp(Of T)(ByVal first() As T, ByVal second() As T) As Int32
        Return memcmp(first, second, AddressOf memcmp)
    End Function

    Public Function memcmp(ByVal first() As Byte, ByVal second() As Byte) As Int32
        Return memcmp(first, second, AddressOf memcmp)
    End Function

    Public Function memcmp(ByVal first() As Int16, ByVal second() As Int16) As Int32
        Return memcmp(first, second, AddressOf memcmp)
    End Function

    Public Function memcmp(ByVal first() As Int32, ByVal second() As Int32) As Int32
        Return memcmp(first, second, AddressOf memcmp)
    End Function

    Public Function memcmp(ByVal first() As Int64, ByVal second() As Int64) As Int32
        Return memcmp(first, second, AddressOf memcmp)
    End Function

    Public Function memcmp(ByVal first() As SByte, ByVal second() As SByte) As Int32
        Return memcmp(first, second, AddressOf memcmp)
    End Function

    Public Function memcmp(ByVal first() As UInt16, ByVal second() As UInt16) As Int32
        Return memcmp(first, second, AddressOf memcmp)
    End Function

    Public Function memcmp(ByVal first() As UInt32, ByVal second() As UInt32) As Int32
        Return memcmp(first, second, AddressOf memcmp)
    End Function

    Public Function memcmp(ByVal first() As UInt64, ByVal second() As UInt64) As Int32
        Return memcmp(first, second, AddressOf memcmp)
    End Function

    Public Function memcmp(ByVal first() As Single, ByVal second() As Single) As Int32
        Return memcmp(first, second, AddressOf memcmp)
    End Function

    Public Function memcmp(ByVal first() As Double, ByVal second() As Double) As Int32
        Return memcmp(first, second, AddressOf memcmp)
    End Function

    Public Function memcmp(ByVal first() As String, ByVal second() As String) As Int32
        Return memcmp(first, second, AddressOf memcmp)
    End Function

    <Extension()> Public Sub append(Of T)(ByRef this() As T, ByVal ParamArray that() As T)
        this = combine(this, that)
    End Sub

    <Extension()> Public Function combine(Of T)(ByVal this() As T, ByVal ParamArray that() As T) As T()
        Dim r() As T = Nothing
        ReDim r(array_size(this) + array_size(that) - 1)
        memcpy(r, this)
        memcpy(r, array_size(this), that)
        Return r
    End Function

    <Extension()> Public Function to_string(Of T)(ByVal this() As T,
                                                  Optional ByVal limited_length As Int32 = 8,
                                                  Optional ByVal separator As String = ", ",
                                                  Optional ByVal ellipsis As String = " ...") As String
        If isemptyarray(this) OrElse limited_length <= 0 Then
            Return Nothing
        Else
            Dim s As StringBuilder = Nothing
            s = New StringBuilder()
            Dim i As Int32 = 0
            For i = 0 To min(array_size(this), limited_length) - 1
                If i > 0 AndAlso Not String.IsNullOrEmpty(separator) Then
                    s.Append(separator)
                End If
                s.Append(Convert.ToString(this(i)))
            Next
            If i < array_size(this) AndAlso Not String.IsNullOrEmpty(ellipsis) Then
                s.Append(ellipsis)
            End If
            Return Convert.ToString(s)
        End If
    End Function
End Module
